﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using FCS.Communication;
using System.Text.RegularExpressions;
using System.Windows.Threading;
using System.Net;
using FCS.LMOC.FountainConnection;
using System.IO;

namespace FCS.LMOC
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        FountainConnection.CPQClient _connection;

        List<string> _activeValves = new List<string>();
        List<string> _restrictedValves = new List<string>();

        string _code;

        Thread _updateFountainState;
        Thread _updateControlLabel;
        Thread _updateEnvrionLabel;
        Thread _updateRestrictedLabel;

        Dictionary<string, Valve> _valves = new Dictionary<string, Valve>();
        Dictionary<string, Valve> _crioStatus = new Dictionary<string, Valve>();

        Label _controlLabel;
        TextBox _LMOCRestricted;
        TextBox _musicRestricted;
        TextBox _koiskRestricted;
        TextBox _phoneRestricted;
        TextBox _bollardRestricted;
        TextBox _patternRestricted;

        bool _isProgramDone = false;
        object _monitor = new object();

        /// <summary>
        /// Connects the lmoc to the server and sets up the the main tab
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();
            CreateDots();
            CreateLabels();
            CreateButtons();
            ReadCode();
            try
            {
                _connection = new FountainConnection.CPQClient();
                _connection.SetDefaultCRIOAddress(_code);

                _updateFountainState = new Thread(() => { GetCurrentFountainState(); });
                _updateControlLabel = new Thread(() => { UpdateControlLabel(); });
                _updateEnvrionLabel = new Thread(() => { UpdateEnvrionLables(); });
                _updateRestrictedLabel = new Thread(() => { UpdateRestrictedValveLabel(); });

                _updateFountainState.Start();
                _updateControlLabel.Start();
                _updateEnvrionLabel.Start();
                _updateRestrictedLabel.Start();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error");
                MessageBox.Show(ex.InnerException.Message, "Inner Exception");
                Close();
            }
        }

        private void ReadCode()
        {
            StreamReader sr = new StreamReader(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86) + @"\Enlight Fountain Control Group\Fountain Control Service\LMOCcode.txt");
            _code = sr.ReadLine();
            sr.Close();
            sr.Dispose();
        }

        /// <summary>
        /// Get the restricted valves for all priorities
        /// </summary>
        private void UpdateRestrictedValveLabel()
        {
            List<string> lmoc = new List<string>();
            List<string> music = new List<string>();
            List<string> kiosk = new List<string>();
            List<string> phone = new List<string>();
            List<string> bollard = new List<string>();
            List<string> patterns = new List<string>();

            try
            {
                lock (_monitor)
                {
                    lmoc = _connection.GetRestrictedValves(GlobalComponents.CommandPriority.lmoc);
                    music = _connection.GetRestrictedValves(GlobalComponents.CommandPriority.music);
                    kiosk = _connection.GetRestrictedValves(GlobalComponents.CommandPriority.kiosk);
                    phone = _connection.GetRestrictedValves(GlobalComponents.CommandPriority.phone);
                    bollard = _connection.GetRestrictedValves(GlobalComponents.CommandPriority.bollards);
                    patterns = _connection.GetRestrictedValves(GlobalComponents.CommandPriority.patterns);
                }
            }
            catch (Exception ex)
            {
            }

            lock (_monitor)
            {
                // print out the restricted valves
                this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
                    {
                        _LMOCRestricted.Text = "";
                        _musicRestricted.Text = "";
                        _koiskRestricted.Text = "";
                        _phoneRestricted.Text = "";
                        _bollardRestricted.Text = "";
                        _patternRestricted.Text = "";

                        foreach (string s in lmoc)
                        {
                            _LMOCRestricted.Text += s + " ";
                        }

                        foreach (string s in music)
                        {
                            _musicRestricted.Text += s + " ";
                        }

                        foreach (string s in kiosk)
                        {
                            _koiskRestricted.Text += s + " ";
                        }

                        foreach (string s in phone)
                        {
                            _phoneRestricted.Text += s + " ";
                        }

                        foreach (string s in bollard)
                        {
                            _bollardRestricted.Text += s + " ";
                        }

                        foreach (string s in patterns)
                        {
                            _patternRestricted.Text += s + " ";
                        }
                    }));
            }
        }

        private void CreateButtons()
        {
            Button r = new Button();
            r.Content = "Release Control";
            r.FontSize = 20;
            ValveCanvas.Children.Add(r);
            Canvas.SetLeft(r, 7);
            Canvas.SetTop(r, 350);

            r.Click += new RoutedEventHandler(ResetButton_click);

            Button off = new Button();
            off.Content = "Shutdown Fountain Valves, Weirs, and Misters";
            off.FontSize = 20;
            ValveCanvas.Children.Add(off);
            Canvas.SetLeft(off, 7);
            Canvas.SetTop(off, 400);

            off.Click += new RoutedEventHandler(OffButton_click);

            Button fetchRestrictedValves = new Button();
            fetchRestrictedValves.Content = "Fetch Restricted Valves";
            fetchRestrictedValves.FontSize = 20;
            fetchRestrictedValves.Click += new RoutedEventHandler(fetchRestrictedValves_Click);
            RestrictedValveCanvas.Children.Add(fetchRestrictedValves);
            Canvas.SetLeft(fetchRestrictedValves, 7);
            Canvas.SetTop(fetchRestrictedValves, 350);
            
            Button updateRestrictedValves = new Button();
            updateRestrictedValves.Content = "Update Values";
            updateRestrictedValves.FontSize = 20;
            updateRestrictedValves.Click += new RoutedEventHandler(updateRestrictedValves_Click);
            RestrictedValveCanvas.Children.Add(updateRestrictedValves);
            Canvas.SetLeft(updateRestrictedValves, 7);
            Canvas.SetTop(updateRestrictedValves, 400);

            
        }

        void fetchRestrictedValves_Click(object sender, RoutedEventArgs e)
        {
            UpdateRestrictedValveLabel();
        }

        /// <summary>
        /// Push the updated restricted valves out to the server
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void updateRestrictedValves_Click(object sender, RoutedEventArgs e)
        {
            // verify valves
            List<string> validValves = new List<string>();

            for (int i = 1; i <= 10; i++)
            {
                validValves.Add("V" + i);
                validValves.Add("H" + i);
            }
            validValves.Add("HR");
            validValves.Add("HC");
            validValves.Add("VR");
            validValves.Add("VC");

            string[] lmoc = _LMOCRestricted.Text.Split(' ');

            foreach (string s in lmoc)
            {
                if(!validValves.Contains(s.Trim()) && !s.Equals(""))
                {
                    MessageBox.Show("Invalid Valve: " + s + " in LMOC");
                    return;
                }
            }

            string[] music = _musicRestricted.Text.Split(' ');
            foreach (string s in music)
            {
                if (!validValves.Contains(s.Trim()) && !s.Equals(""))
                {
                    MessageBox.Show("Invalid Valve: " + s + " in Music");
                    return;
                }
            }

            string[] kiosk = _koiskRestricted.Text.Split(' ');
            foreach (string s in kiosk)
            {
                if (!validValves.Contains(s.Trim()) && !s.Equals(""))
                {
                    MessageBox.Show("Invalid Valve: " + s + " in Kiosk");
                    return;
                }
            }

            string[] phone = _phoneRestricted.Text.Split(' ');
            foreach (string s in phone)
            {
                if (!validValves.Contains(s.Trim()) && !s.Equals(""))
                {
                    MessageBox.Show("Invalid Valve: " + s + " in phone");
                    return;
                }
            }

            string[] bollards = _bollardRestricted.Text.Split(' ');
            foreach (string s in bollards)
            {
                if (!validValves.Contains(s.Trim()) && !s.Equals(""))
                {
                    MessageBox.Show("Invalid Valve: " + s + " in bollards");
                    return;
                }
            }

            string[] pattern = _patternRestricted.Text.Split(' ');
            foreach (string s in pattern)
            {
                if (!validValves.Contains(s.Trim()) && !s.Equals(""))
                {
                    MessageBox.Show("Invalid Valve: " + s + " in pattern");
                    return;
                }
            }
            try
            {
                // fetch and remove old restricted valves 
                lock (_monitor)
                {
                    _connection.RemoveRestrictedValves(_connection.GetRestrictedValves(GlobalComponents.CommandPriority.lmoc), GlobalComponents.CommandPriority.lmoc, _code);
                    _connection.RemoveRestrictedValves(_connection.GetRestrictedValves(GlobalComponents.CommandPriority.music), GlobalComponents.CommandPriority.music, _code);
                    _connection.RemoveRestrictedValves(_connection.GetRestrictedValves(GlobalComponents.CommandPriority.kiosk), GlobalComponents.CommandPriority.kiosk, _code);
                    _connection.RemoveRestrictedValves(_connection.GetRestrictedValves(GlobalComponents.CommandPriority.phone), GlobalComponents.CommandPriority.phone, _code);
                    _connection.RemoveRestrictedValves(_connection.GetRestrictedValves(GlobalComponents.CommandPriority.bollards), GlobalComponents.CommandPriority.bollards, _code);
                    _connection.RemoveRestrictedValves(_connection.GetRestrictedValves(GlobalComponents.CommandPriority.patterns), GlobalComponents.CommandPriority.patterns, _code);
                }

                // add new valves
                _connection.AddRestrictedValves(lmoc.ToList(), GlobalComponents.CommandPriority.lmoc, _code);
                _connection.AddRestrictedValves(music.ToList(), GlobalComponents.CommandPriority.music, _code);
                _connection.AddRestrictedValves(kiosk.ToList(), GlobalComponents.CommandPriority.kiosk, _code);
                _connection.AddRestrictedValves(phone.ToList(), GlobalComponents.CommandPriority.phone, _code);
                _connection.AddRestrictedValves(bollards.ToList(), GlobalComponents.CommandPriority.bollards, _code);
                _connection.AddRestrictedValves(pattern.ToList(), GlobalComponents.CommandPriority.patterns, _code);
            }
            catch (Exception ex)
            { }
        }

        /// <summary>
        /// Turn everything off on the fountain
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OffButton_click(object sender, RoutedEventArgs e)
        {
            List<string> cmd = new List<string>();
            cmd.Add("H*");
            cmd.Add("V*");
            cmd.Add("W*");
            cmd.Add("L*");

            try
            {
                _connection.SendCommand("OFF", cmd, GlobalComponents.CommandPriority.lmoc, _code);
            }
            catch (Exception)
            { }

            lock (_monitor)
            {
                foreach (string s in _activeValves)
                {
                    Fountain_ValveChanged(s, false);
                }
            }
        }

        /// <summary>
        /// Reset the priority threshold on the server
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ResetButton_click(object sender, RoutedEventArgs e)
        {
            try
            {
                _connection.ResetThreshold(GlobalComponents.CommandPriority.lmoc, _code);
            }
            catch (Exception) { }
        }

        /// <summary>
        /// Monitors for the current state of the fountain
        /// </summary>
        private void UpdateEnvrionLables()
        {
            bool isDone = false;

            while (!isDone)
            {
                Dictionary<string, bool> state;

                try
                {
                    lock (_monitor)
                    {
                        state = _connection.GetResourceStatus();
                    }
                }
                catch (Exception) { state = new Dictionary<string, bool>(); }

                foreach (string s in state.Keys)
                {
                    Fountain_EnvrionChanged(s, state[s]);
                }

                Thread.Sleep(2000);

                lock (_monitor)
                {
                    isDone = _isProgramDone;
                }
            }
        }

        /// <summary>
        /// Updates the UI when the environment changes
        /// </summary>
        /// <param name="valve"></param>
        /// <param name="newState"></param>
        void Fountain_EnvrionChanged(string valve, bool newState)
        {
            // We need to update our screen
            Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
            {
                if (_crioStatus.ContainsKey(valve))
                    _crioStatus[valve].State = newState;
            }));
        }

        /// <summary>
        /// Shuts down the threads and resets the threshold back to the patterns so that the fountain is not locked when
        /// the LMOC closes
        /// </summary>
        /// <param name="e"></param>
        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            lock (_monitor)
            {
                _isProgramDone = true;
            }

            if (_updateFountainState != null)
                _updateFountainState.Join(1000);
            if (_updateControlLabel != null)
                _updateControlLabel.Join(1000);

            try
            {
                _connection.ResetThreshold(GlobalComponents.CommandPriority.lmoc, _code);
                _connection.Close();
            }
            catch (Exception) { }


            base.OnClosing(e);
        }

        /// <summary>
        /// Creates the labels for the UI
        /// </summary>
        private void CreateLabels()
        {
            #region Valves Labels
            // create the labels that run above the valves
            for (int i = 0; i < 12; i++)
            {
                Label l = new Label();
                l.FontSize = 20;
                l.Content = i + 1;

                if (i == 10)
                    l.Content = "C";
                else if (i == 11)
                    l.Content = "R";

                ValveCanvas.Children.Add(l);
                if (i != 9)
                    Canvas.SetLeft(l, 39 + 50 * i);
                else if (i == 9)
                    Canvas.SetLeft(l, 34 + 50 * i);


                Canvas.SetTop(l, 5);
            }

            // create the H and V labels to the left of the valve markers
            Label h = new Label();
            Label v = new Label();

            h.FontSize = 20;
            h.Content = "H";
            ValveCanvas.Children.Add(h);
            Canvas.SetLeft(h, 7);
            Canvas.SetTop(h, 30);

            v.FontSize = 20;
            v.Content = "V";
            ValveCanvas.Children.Add(v);
            Canvas.SetLeft(v, 7);
            Canvas.SetTop(v, 80);

            // create N label for north end mister
            Label n = new Label();
            n.FontSize = 20;
            n.Content = "N";
            ValveCanvas.Children.Add(n);
            Canvas.SetLeft(n, 7);
            Canvas.SetTop(n, 130);
            #endregion

            #region Winter Mister Labels
            // create labels to indicate winter mister valve
            Label m = new Label();
            m.FontSize = 20;
            m.Content = "M";
            ValveCanvas.Children.Add(m);
            Canvas.SetLeft(m, 7);
            Canvas.SetTop(m, 230);

            // create labels for north and south mister
            Label mn = new Label();
            mn.FontSize = 20;
            mn.Content = "N";
            ValveCanvas.Children.Add(mn);
            Canvas.SetLeft(mn, 37);
            Canvas.SetTop(mn, 260);

            Label ms = new Label();
            ms.FontSize = 20;
            ms.Content = "S";
            ValveCanvas.Children.Add(ms);
            Canvas.SetLeft(ms, 89);
            Canvas.SetTop(ms, 260);

            #endregion

            #region Weir and Bollard Labels
            // create the W label to the left of the weir markers
            Label w = new Label();
            w.FontSize = 20;
            w.Content = "W";
            ValveCanvas.Children.Add(w);
            Canvas.SetLeft(w, 7);
            Canvas.SetTop(w, 180);

            // create bollard label
            Label b = new Label();
            b.FontSize = 20;
            b.Content = "B";
            EnvrionmentalCanvas.Children.Add(b);
            Canvas.SetLeft(b, 7);
            Canvas.SetTop(b, 30);

            // create the labels that run above the bollards
            for (int i = 0; i < 5; i++)
            {
                Label l = new Label();
                l.FontSize = 20;
                l.Content = i + 1;
                EnvrionmentalCanvas.Children.Add(l);
                Canvas.SetLeft(l, 39 + 50 * i);
                Canvas.SetTop(l, 5);
            }

            #endregion

            // create who is in control label
            _controlLabel = new Label();
            _controlLabel.FontSize = 20;
            _controlLabel.Content = "Control: ?";
            ValveCanvas.Children.Add(_controlLabel);
            Canvas.SetLeft(_controlLabel, 7);
            Canvas.SetTop(_controlLabel, 300);

            // create pump label
            Label p = new Label();
            p.FontSize = 20;
            p.Content = "P";
            EnvrionmentalCanvas.Children.Add(p);
            Canvas.SetLeft(p, 7);
            Canvas.SetTop(p, 80);

            // create envrion labels
            for (int i = 0; i < 4; i++)
            {
                Label l = new Label();
                l.FontSize = 20;
                if (i == 0)
                    l.Content = "Manhole Cover Closed";
                else if (i == 1)
                    l.Content = "Sidewalk Water Level OK";
                else if (i == 2)
                    l.Content = "South End Hardware Disable";
                else if (i == 3)
                    l.Content = "North End Hardware Disable";

                EnvrionmentalCanvas.Children.Add(l);
                Canvas.SetLeft(l, 7);
                Canvas.SetTop(l, i * 50 + 130);
            }

            #region Restricted Labels

            // The labels
            for (int i = 0; i < 6; i++)
            {
                Label l = new Label();
                lock (_monitor)
                {
                    if (i == 0)
                        l.Content = "LMOC";
                    else if (i == 1)
                        l.Content = "Music";
                    else if (i == 2)
                        l.Content = "Kiosk";
                    else if (i == 3)
                        l.Content = "Phone";
                    else if (i == 4)
                        l.Content = "Bollards";
                    else if (i == 5)
                        l.Content = "Patterns";

                    l.FontSize = 20;
                    RestrictedValveCanvas.Children.Add(l);
                    Canvas.SetTop(l, 39 + 50 * i);
                    Canvas.SetLeft(l, 5);
                }
            }

            // the restricted valves
            for (int i = 0; i < 6; i++)
            {
                TextBox l = new TextBox();
                if (i == 0)
                {
                    l.Text = "LMOC 2";
                    _LMOCRestricted = l;
                }
                else if (i == 1)
                {
                    l.Text = "Music";
                    _musicRestricted = l;
                }
                else if (i == 2)
                {
                    l.Text = "Kiosk";
                    _koiskRestricted = l;
                }
                else if (i == 3)
                {
                    l.Text = "Phone";
                    _phoneRestricted = l;
                }
                else if (i == 4)
                {
                    l.Text = "Bollards";
                    _bollardRestricted = l;
                }
                else if (i == 5)
                {
                    l.Text = "Patterns";
                    _patternRestricted = l;
                }

                l.Width = 500;

                l.FontSize = 20;
                RestrictedValveCanvas.Children.Add(l);
                Canvas.SetTop(l, 39 + 50 * i);
                Canvas.SetLeft(l, 150);
            }

            #endregion
        }

        /// <summary>
        /// Updates the UI when control of the fountain changes to 
        /// a different priority
        /// </summary>
        private void UpdateControlLabel()
        {
            bool isDone = false;

            while (!isDone)
            {
                GlobalComponents.CommandPriority currentlyInControl = GlobalComponents.CommandPriority.lmoc;
                string text = "Control: ";

                lock (_monitor)
                {
                    try
                    {
                        currentlyInControl = _connection.GetThreshold();
                    }
                    catch
                    {
                        _connection = null;
                        _connection = new CPQClient();
                    }
                }

                switch (currentlyInControl)
                {
                    case GlobalComponents.CommandPriority.lmoc:
                        text += "LMOC";
                        break;
                    case GlobalComponents.CommandPriority.music:
                        text += "Music";
                        break;
                    case GlobalComponents.CommandPriority.kiosk:
                        text += "Kiosk";
                        break;
                    case GlobalComponents.CommandPriority.phone:
                        text += "Phone";
                        break;
                    case GlobalComponents.CommandPriority.bollards:
                        text += "Bollards";
                        break;
                    case GlobalComponents.CommandPriority.patterns:
                        text += "Patterns";
                        break;
                    default:
                        text += "Unknown priority!";
                        break;
                }

                lock (_monitor)
                {
                    this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
                        {
                            ValveCanvas.Children.Remove(_controlLabel);
                            _controlLabel.Content = text;
                            ValveCanvas.Children.Add(_controlLabel);
                        }));
                }

                Thread.Sleep(2000);



                lock (_monitor)
                {
                    isDone = _isProgramDone;
                }
            }
        }
        
        /// <summary>
        /// Get the restricted valves for the LMOC
        /// </summary>
        private void GetRestrictedValves()
        {
            try
            {
                lock (_monitor)
                {
                    _restrictedValves = _connection.GetRestrictedValves(GlobalComponents.CommandPriority.lmoc);
                }
            }
            catch (Exception)
            { }

            foreach (string s in _restrictedValves)
            {
                Fountain_ValveDisabled(s, true);
            }
        }


        /// <summary>
        /// Gets the current state from the fountain and update the view
        /// </summary>
        private void GetCurrentFountainState()
        {
            bool isDone = false;

            while (!isDone)
            {
                List<string> serverValveList = new List<string>();
                lock (_monitor)
                {
                    try
                    {
                        serverValveList = _connection.GetActiveValves();
                        serverValveList.Concat(_connection.GetActiveWeirs());
                    }
                    catch (Exception)
                    {
                        serverValveList = new List<string>();
                    }
                }

                // get the newly activated valves
                foreach (string s in serverValveList)
                {
                    Fountain_ValveChanged(s, true);
                }

                // disable valves not in the new state
                foreach (string s in _activeValves)
                {
                    if (!serverValveList.Contains(s))
                    {
                        Fountain_ValveChanged(s, false);
                    }
                }

                _activeValves = serverValveList;

                lock (_monitor)
                {
                    isDone = _isProgramDone;
                }
            }
        }

        /// <summary>
        /// Shuts off all of the valves on the fountain before restoring control to the lower priority 
        /// </summary>
        private void ResetFountainValves()
        {
            lock (_monitor)
            {
                List<string> valves = new List<string>();
                try
                {
                    valves = _connection.GetActiveValves();
                    _connection.SendCommand("OFF", valves, GlobalComponents.CommandPriority.lmoc, _code);
                }
                catch (Exception) { }
                foreach (string s in _activeValves)
                {
                    Fountain_ValveChanged(s, false);
                }

                foreach (string s in _restrictedValves)
                {
                    Fountain_ValveDisabled(s, false);
                }

                _activeValves.Clear();
            }
        }

        /// <summary>
        /// Allows for the real-time-ish monitoring of the fountain
        /// </summary>
        /// <param name="valve"></param>
        /// <param name="newState"></param>
        void Fountain_ValveChanged(string valve, bool newState)
        {
            // We need to update our screen
            Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
            {
                if (_valves.ContainsKey(valve))
                    _valves[valve].State = newState;
            }));
        }

        void Fountain_ValveDisabled(string valve, bool isDisabled)
        {
            Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
            {
                if (_valves.ContainsKey(valve))
                    _valves[valve].Disabled = isDisabled;
            }));
        }

        /// <summary>
        /// Called by the constructor to handle the creation of the dots.
        /// </summary>
        private void CreateDots()
        {
            #region Create Valves
            Action<Valve> AddValve = (Valve v) => { _valves.Add(v.Name, v); };

            AddValve(new Valve("V1", 50, 100));
            AddValve(new Valve("V2", 100, 100));
            AddValve(new Valve("V3", 150, 100));
            AddValve(new Valve("V4", 200, 100));
            AddValve(new Valve("V5", 250, 100));
            AddValve(new Valve("V6", 300, 100));
            AddValve(new Valve("V7", 350, 100));
            AddValve(new Valve("V8", 400, 100));
            AddValve(new Valve("V9", 450, 100));
            AddValve(new Valve("V10", 500, 100));
            AddValve(new Valve("VR", 550, 100));
            AddValve(new Valve("VC", 600, 100));

            AddValve(new Valve("H1", 50, 50));
            AddValve(new Valve("H2", 100, 50));
            AddValve(new Valve("H3", 150, 50));
            AddValve(new Valve("H4", 200, 50));
            AddValve(new Valve("H5", 250, 50));
            AddValve(new Valve("H6", 300, 50));
            AddValve(new Valve("H7", 350, 50));
            AddValve(new Valve("H8", 400, 50));
            AddValve(new Valve("H9", 450, 50));
            AddValve(new Valve("H10", 500, 50));
            AddValve(new Valve("HR", 550, 50));
            AddValve(new Valve("HC", 600, 50));

            AddValve(new Valve("N0", 50, 150));

            AddValve(new Valve("MN", 50, 250));
            AddValve(new Valve("MS", 100, 250));

            AddValve(new Valve("W1", 50, 200));
            AddValve(new Valve("W2", 100, 200));
            AddValve(new Valve("W3", 150, 200));

            foreach (Valve valve in _valves.Values)
            {
                ValveCanvas.Children.Add(valve.Dot);
                Canvas.SetLeft(valve.Dot, valve.X - Valve.DotSize / 2);
                Canvas.SetTop(valve.Dot, valve.Y - Valve.DotSize / 2);
            }
            #endregion

            #region Create Envrionmental Dots
            Action<Valve> AddCRIOStatus = (Valve v) => { _crioStatus.Add(v.Name, v); };

            AddCRIOStatus(new Valve("B0", 50, 50));
            AddCRIOStatus(new Valve("B1", 100, 50));
            AddCRIOStatus(new Valve("B2", 150, 50));
            AddCRIOStatus(new Valve("B3", 200, 50));
            AddCRIOStatus(new Valve("B4", 250, 50));

            AddCRIOStatus(new Valve("P0", 50, 100));
            AddCRIOStatus(new Valve("P1", 100, 100));
            AddCRIOStatus(new Valve("P2", 150, 100));
            AddCRIOStatus(new Valve("P3", 200, 100));

            AddCRIOStatus(new Valve("Manhole Cover Closed", 300, 150));
            AddCRIOStatus(new Valve("Sidewalk Water Level OK", 300, 200));
            AddCRIOStatus(new Valve("South End HW Disable", 300, 250));
            AddCRIOStatus(new Valve("North End HW Disable", 300, 300));

            foreach (Valve valve in _crioStatus.Values)
            {
                EnvrionmentalCanvas.Children.Add(valve.Dot);
                Canvas.SetLeft(valve.Dot, valve.X - Valve.DotSize / 2);
                Canvas.SetTop(valve.Dot, valve.Y - Valve.DotSize / 2);
            }

            #endregion
        }

        /// <summary>
        /// When the user presses down with their finger (i.e. mouse) the valve should be toggled
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FountainControl_MouseDown(object sender, MouseButtonEventArgs e)
        {
            GetRestrictedValves();

            Valve v = GetValveFromPoint(e);
            if (v != null)
            {
                List<string> cmd = new List<string>();
                cmd.Add(v.Name);
                string state = !v.State ? "ON" : "OFF";

                bool validChange; // checks to make sure that the command was successfully sent
                lock (_monitor)
                {
                    // regular expression that checks to see if the command is for the weirs or valves
                    Regex weirChecker = new Regex("W+");
                    Regex winterMisterChecker = new Regex("M+");
                    /*
                    if (weirChecker.IsMatch(cmd.ElementAt(0)))
                        validChange = _connection.SendWeirCommand(state, cmd, GlobalComponents.CommandPriority.lmoc);
                    else if (winterMisterChecker.IsMatch(cmd.ElementAt(0)))
                        validChange = _connection.SendWinterCommand(state, cmd, GlobalComponents.CommandPriority.lmoc);
                    else
                        validChange = _connection.SendValveCommand(state, cmd, GlobalComponents.CommandPriority.lmoc);*/
                    try
                    {
                        validChange = _connection.SendCommand(state, cmd, GlobalComponents.CommandPriority.lmoc, _code);
                    }
                    catch (Exception ex)
                    {
                        validChange = false;
                    }
                }

                if (validChange)
                {
                    Fountain_ValveChanged(v.Name, !v.State);
                    _activeValves.Add(v.Name);
                }
            }
        }

        /// <summary>
        /// Find the valve that is closest to where the mouse is
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        private Valve GetValveFromPoint(MouseEventArgs e)
        {
            Point point = e.GetPosition(ValveCanvas);

            // Let's find the closest point to our tap
            Valve valve = _valves.Values.OrderBy(v => v.GetDistanceFromPointSquared(point)).First();

            // Have a minimum closeness
            if (valve.GetDistanceFromPointSquared(point) < Math.Pow(Valve.DotSize + 20, 2))
                return valve;
            else
                return null;
        }
    }

    public enum RobustResourceStatus
    {
        /// <summary>
        /// The robust connection is not currently connected to the server.
        /// </summary>
        Disconnected,

        /// <summary>
        /// The robust connection is connected, but there is no request for the given resource.
        /// </summary>
        Idling,

        /// <summary>
        /// The robust connection is controlling the resource.
        /// </summary>
        Controlling,

        /// <summary>
        /// Another higher-priority user is controlling the resource.
        /// </summary>
        Preempted,

        /// <summary>
        /// The robust connection is waiting for another user of the same priority to finish.
        /// </summary>
        Waiting,

        /// <summary>
        /// The fountain is off, and so the resource is not available.
        /// </summary>
        FountainOff
    }
}
